package com.example.stock.service.impl;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.example.stock.entity.dao.CreateDao;
import com.example.stock.entity.dao.HistoryDao;
import com.example.stock.entity.dao.RuleDao;
import com.example.stock.entity.dao.UnitDao;
import com.example.stock.entity.eo.CreateEo;
import com.example.stock.entity.eo.HistoryEo;
import com.example.stock.entity.eo.RuleEo;
import com.example.stock.entity.eo.UnitEo;
import com.example.stock.service.IRuleService;
import com.example.stock.service.IUnitService;
import com.example.stock.vo.PlusVo;
import com.example.stock.vo.UnitCreateForm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.*;
import java.util.Comparator;
import java.util.List;

@Service
@Slf4j
public class UnitServiceImpl implements IUnitService {

    @Resource
    private RuleDao ruleDao;
    @Resource
    private UnitDao unitDao;
    @Resource
    private IRuleService ruleService;
    @Resource
    private HistoryDao historyDao;
    @Resource
    private CreateDao createDao;


    @Override
    public UnitEo calcDown(UnitEo unit) {
        RuleEo ruleEo = ruleDao.getById(unit.getRuId());
        Assert.notNull(ruleEo, "ru_id不存在");

        UnitEo unitEo = new UnitEo();
        unitEo.setRuId(unit.getRuId());
        unitEo.setTrGroup(unit.getTrGroup());
        unitEo.setStName(unit.getStName());
        unitEo.setStCode(unit.getStCode());
        unitEo.setTrUnit(unit.getTrUnit());
        unitEo.setTrStep(unit.getTrStep() - 1);

        RuleEo.UpDnCalc upDnCalc = ruleService.getUpDnCalc(ruleEo, unit.getTrStep());
        Double dnPercent = upDnCalc.getDnPercent();
        Double trUnit = unitEo.getTrUnit();
        if (dnPercent != null) {

            //现价 = 前价 * 跌幅
            unitEo.setTrPrice(unit.getTrPrice() * (1 - dnPercent / 100));

            //增量股数
            double 数量 = trUnit / unitEo.getTrPrice();
            double 增量 = 数量 + unit.getTrPlus();
            PlusVo plusVo = PlusVo.getPlusVo(增量);

            //持股 = 前股 + 增量
            unitEo.setStQty(unit.getStQty() + plusVo.getTrQty());
            unitEo.setTrPlus(plusVo.getTrPlus());

            //计算成本
            double stPrice = (unit.getStPrice() * unit.getStQty() + plusVo.getTrQty() * unitEo.getTrPrice()) / unitEo.getStQty();
            unitEo.setStPrice(stPrice);
            unitEo.setTrDate(LocalDateTime.now());

        } else {

            RuleEo.UpDnCalc upDnCalc_1 = ruleService.getUpDnCalc(ruleEo, unit.getTrStep() - 1);
            unitEo.setTrPrice(unit.getTrPrice() * (1 - upDnCalc_1.getUpPercent() / 100));
            double totalMoney = trUnit * (ruleEo.getTopStep() - unit.getTrStep() + 1);

            double 数量 = totalMoney / unitEo.getTrPrice();

            PlusVo plusVo = PlusVo.getPlusVo(数量);

            unitEo.setStQty((double) plusVo.getTrQty());

            unitEo.setTrPlus(plusVo.getTrPlus());

            double stQty = (unit.getStPrice() * unit.getStQty() + (unitEo.getStQty() - unit.getStQty()) * unitEo.getTrPrice()) / unitEo.getStQty();
            unitEo.setStPrice(stQty);
            unitEo.setTrDate(LocalDateTime.now());
        }

        return unitEo;

    }

    @Override
    public UnitEo calcUp(UnitEo unit) {
        RuleEo ruleEo = ruleDao.getById(unit.getRuId());
        Assert.notNull(ruleEo, "ru_id不存在");

        UnitEo unitEo = new UnitEo();
        unitEo.setRuId(unit.getRuId());
        unitEo.setTrGroup(unit.getTrGroup());
        unitEo.setStName(unit.getStName());
        unitEo.setStCode(unit.getStCode());
        unitEo.setTrUnit(unit.getTrUnit());
        unitEo.setTrStep(unit.getTrStep() + 1);

        if (unit.getTrStep() >= ruleEo.getTopStep()) {
            log.info("===============已经清仓==============");
            return null;
        }

        RuleEo.UpDnCalc upDnCalc = ruleService.getUpDnCalc(ruleEo, unit.getTrStep());

        Double upPercent = upDnCalc.getUpPercent();
        Double trUnit = unit.getTrUnit();
        if (upPercent != null) {


            //涨后价格
            unitEo.setTrPrice(unit.getTrPrice() * (1 + upPercent / 100));

            double totalMoney = trUnit * (ruleEo.getTopStep() - unit.getTrStep() - 1);

            double 余量 = totalMoney / unitEo.getTrPrice();

            double 卖出股数 = unit.getStQty() - 余量;

            PlusVo plusVo = PlusVo.getPlusVo(卖出股数);
            double trPlus = plusVo.getTrPlus();
            if (trPlus > 0) {
                plusVo.setTrQty(plusVo.getTrQty() + 100);
                plusVo.setTrPlus(100 - plusVo.getTrPlus());
            }

            unitEo.setStQty(unit.getStQty() - plusVo.getTrQty());
            unitEo.setTrPlus(plusVo.getTrPlus());

            double stPrice = (unit.getStPrice() * unit.getStQty() - (unit.getStQty() - unitEo.getStQty()) * unitEo.getTrPrice()) / unitEo.getStQty();
            unitEo.setStPrice(stPrice);
            unitEo.setTrDate(LocalDateTime.now());
        } else {

            //计算上次跌幅
            RuleEo.UpDnCalc upDnCalc_1 = ruleService.getUpDnCalc(ruleEo, unit.getTrStep() + 1);

            double totalMoney = (unit.getStQty() + unit.getTrPlus()) * unit.getStPrice();

            //上次跌后市值
            double totalMoney_1 = totalMoney - unit.getTrUnit();

            //上次跌幅 阶梯=-1, 上次跌幅为 (-3%)
            Double dnPercent = upDnCalc_1.getDnPercent();

            //上次跌前市值
            double totalMoney1 = totalMoney_1 / (1 - dnPercent / 100);
            //上次亏损
            double dnMoney = totalMoney1 - totalMoney_1;

            //当前涨幅
            Double upPercent1 = dnMoney / totalMoney;

            unitEo.setTrPrice(unit.getTrPrice() * (1 + upPercent1));

            //卖出股数
            double totalQty = unit.getTrUnit() / unitEo.getTrPrice() - unit.getTrPlus();
            PlusVo plusVo = PlusVo.getPlusVo(totalQty);

            if (plusVo.getTrPlus() > 0) {
                plusVo.setTrQty(plusVo.getTrQty() + 100);
                plusVo.setTrPlus(100 - plusVo.getTrPlus());
            }


            unitEo.setStQty((unit.getStQty() - plusVo.getTrQty()));
            unitEo.setTrPlus(plusVo.getTrPlus());

            double stQty = (unit.getStPrice() * unit.getStQty() - plusVo.getTrQty() * unitEo.getTrPrice()) / unitEo.getStQty();
            unitEo.setStPrice(stQty);
            unitEo.setTrDate(LocalDateTime.now());
        }
        return unitEo;

    }

    @Override
    public UnitEo.DownVo getDownVo(UnitEo unit) {

        UnitEo unitEo = calcDown(unit);

        UnitEo.DownVo downVo = new UnitEo.DownVo();

        Double trPrice = unitEo.getTrPrice();
        downVo.setTrPrice(NumberUtil.round(trPrice, 6).doubleValue());
        downVo.setTrQty((unitEo.getStQty() - unit.getStQty()));

        //a2 = a1(1-5%)
        //
        //(1-A2/a1)*100 = 5%
        double dnPercent = (1 - unitEo.getTrPrice() / unit.getTrPrice()) * 100;
        downVo.setDnPercent(NumberUtil.round(dnPercent, 6).doubleValue());

        double dnMoney = unit.getStQty() * unit.getStPrice() * downVo.getDnPercent() / 100;
        downVo.setDnMoney(NumberUtil.round(dnMoney, 6).doubleValue());
        return downVo;


    }

    @Override
    public UnitEo.UpVo getUpVo(UnitEo unit) {

        UnitEo unitEo = calcUp(unit);

        UnitEo.UpVo downVo = new UnitEo.UpVo();
        if (null == unitEo) {
            return downVo;
        }

        Double trPrice = unitEo.getTrPrice();
        downVo.setTrPrice(NumberUtil.round(trPrice, 6).doubleValue());
        downVo.setTrQty((unit.getStQty() - unitEo.getStQty()));

        //a2 = a1(1+5%)
        //(A2/a1-1)*100 = 5%
        double upPercent = (unitEo.getTrPrice() / unit.getTrPrice() - 1) * 100;
        downVo.setUpPercent(NumberUtil.round(upPercent, 6).doubleValue());
        double upMoney = unit.getStQty() * unit.getStPrice() * downVo.getUpPercent() / 100;
        downVo.setUpMoney(NumberUtil.round(upMoney, 6).doubleValue());
        return downVo;


    }

    @Override
    public void remove(Integer trGroup, Double trEarn) {
        LambdaQueryWrapper<UnitEo> eq = Wrappers.<UnitEo>lambdaQuery().eq(UnitEo::getTrGroup, trGroup);
        List<UnitEo> list = unitDao.list(eq);
        UnitEo max = list.stream().max(Comparator.comparing(UnitEo::getId)).get();
        UnitEo min = list.stream().min(Comparator.comparing(UnitEo::getId)).get();
        HistoryEo historyEo = new HistoryEo();
        historyEo.setRuId(max.getRuId());
        historyEo.setTrGroup(max.getTrGroup());
        historyEo.setTrPlus(max.getTrPlus());
        historyEo.setStName(max.getStName());
        historyEo.setStCode(max.getStCode());
        historyEo.setTrPrice(max.getTrPrice());
        historyEo.setStPrice(max.getStPrice());
        historyEo.setStQty(max.getStQty());
        historyEo.setTrStep(max.getTrStep());
        historyEo.setTrUnit(max.getTrUnit());
        historyEo.setTrDate(LocalDateTime.now());
        historyEo.setMark(max.getMark());
        historyEo.setRuName(ruleDao.getById(max.getRuId()).getRuName());
        historyEo.setTrEarn(trEarn);
        LocalDateTime tiStart = toDate(min.getTrDate());
        historyEo.setTiStart(tiStart);
        LocalDateTime tiEnd = toDate(LocalDateTime.now());
        historyEo.setTiEnd(tiEnd);
        long days = Math.abs(historyEo.getTiStart().toLocalDate().toEpochDay()- historyEo.getTiEnd().toLocalDate().toEpochDay());
//        int days = Period.between(historyEo.getTiStart().toLocalDate(), historyEo.getTiEnd().toLocalDate()).getDays();
        historyEo.setTiDay((int)days);
        historyDao.save(historyEo);
        unitDao.remove(eq);
    }

    private static LocalDateTime toDate(LocalDateTime localDateTime) {
        LocalDate localDate = localDateTime.toLocalDate();
        return LocalDateTime.of(localDate, LocalTime.MIN);
    }

    @Override
    public void create(UnitCreateForm createForm) {
        RuleEo byId = ruleDao.getById(createForm.getRuId());
        Assert.notNull(byId, "规则不存在");

        UnitEo unitEo = new UnitEo();
        unitEo.setStCode(createForm.getStCode());
        unitEo.setTrUnit(createForm.getTrUnit());
        unitEo.setTrPrice(createForm.getTrPrice());
        unitEo.setStName(createForm.getStName());
        unitEo.setRuId(createForm.getRuId());
        unitEo.setStPrice(createForm.getTrPrice());

        double v = createForm.getTrUnit() * byId.getTopStep() / createForm.getTrPrice();

        PlusVo plusVo = PlusVo.getPlusVo(v);
        unitEo.setStQty((double) plusVo.getTrQty());
        unitEo.setTrPlus(plusVo.getTrPlus());

        Integer maxId = unitDao.getMaxId();
        unitEo.setTrGroup(++maxId);
        unitEo.setTrStep(0);
        unitEo.setTrDate(LocalDateTime.now());
        unitEo.setMark(1);
        unitDao.save(unitEo);

        CreateEo createEo = new CreateEo();
        createEo.setStName(unitEo.getStName());
        createEo.setStCode(unitEo.getStCode());
        createEo.setStPrice(unitEo.getStPrice());
        createEo.setStQty(unitEo.getStQty());
        createEo.setTrUnit(unitEo.getTrUnit());
        createEo.setTrDate(unitEo.getTrDate());
        createEo.setRuId(unitEo.getRuId());
        createEo.setRuName(byId.getRuName());
        createDao.save(createEo);

    }
}
